package com.leehom.arch.base.redis.client.config;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leehom.arch.base.redis.client.RedisClient;

import redis.clients.jedis.JedisPoolConfig;

/**
 * 类名: RedisClientConfig
 * 说明: redis client自动配置
 *          
 *
 * @author: leehom
 * @Date	2018年11月3日 上午9:47:13
 * 修改记录：
 *
 * @see
 * 
 * TODO
 *   *. 增加Conditional
 *   *. 支持cluster
 *     
 */
@Configuration
public class RedisClientConfig {

    @Value("${redis.hosts}")
    private String redisHosts;
    @Value("${redis.master.name}")
    private String master;
    @Value("${redis.pool.maxIdle:300}")
    private int maxIdle;
    @Value("${redis.pool.minIdle:10}")
    private int minIdle;
    @Value("${redis.pool.maxActive:600}")
    private int maxTotal;
    @Value("${redis.pool.maxWait:3000}")
    private int maxWaitMillis;
    @Value("${redis.pool.testOnBorrow:true}")
    private boolean testOnBorrow;
    @Value("${redis.password}")
    private String password;
    @Value("${redis.database:0}")
    private int database;
   
    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setTestOnBorrow(testOnBorrow);
        return jedisPoolConfig;
    }
    
    @Bean
    @ConditionalOnProperty(prefix = "redis", name = {"mode"}, havingValue = "single", matchIfMissing=true)
    public RedisStandaloneConfiguration redisStandaloneConfiguration() {
    	RedisStandaloneConfiguration conf = new RedisStandaloneConfiguration();
    	//
    	String host = redisHosts.trim();
    	String[] item = host.split(":");
		String ip = item[0];
		String port = item[1];
    	conf.setHostName(ip);
    	conf.setPort(Integer.parseInt(port));
    	conf.setDatabase(database);
    	conf.setPassword(RedisPassword.of(password));
    	return conf;
    }
    
    @Bean
    @ConditionalOnProperty(prefix = "redis", name = {"mode"}, havingValue = "sentinel")
    public RedisSentinelConfiguration redisSentinelConfiguration() {
    	RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
    	String[] host = redisHosts.split(",");
    	for(String redisHost : host){
    		String[] item = redisHost.split(":");
    		String ip = item[0];
    		String port = item[1];
    		redisSentinelConfiguration.addSentinel(new RedisNode(ip, Integer.parseInt(port)));
    	}
    	if (StringUtils.isNotBlank(password)) {
    		redisSentinelConfiguration.setPassword(RedisPassword.of(password));
    	}
    	redisSentinelConfiguration.setMaster(master);
    	redisSentinelConfiguration.setDatabase(database);
    	return redisSentinelConfiguration;
    }
    
    @Bean(name="keySer")
    @ConditionalOnMissingBean(name="keySer")
    public RedisSerializer<Object> keyRedisSerializer() {
    	Jackson2JsonRedisSerializer<Object> ser = new Jackson2JsonRedisSerializer<Object>(Object.class);
    	//
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //
        ser.setObjectMapper(om);
    	return ser;
    }
    
    @Bean(name="valueSer")
    @ConditionalOnMissingBean(name="valueSer")
    public RedisSerializer<Object> valueRedisSerializer() {
    	Jackson2JsonRedisSerializer<Object> ser = new Jackson2JsonRedisSerializer<Object>(Object.class);
    	//
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //
        ser.setObjectMapper(om);
    	return ser;
    }
    
    @Bean
    @ConditionalOnBean(RedisSentinelConfiguration.class)
    public JedisConnectionFactory jedisSentinelConnectionFactory(
    			RedisSentinelConfiguration redisSentinelConfiguration, JedisPoolConfig jedisPoolConfig) {
    	JedisConnectionFactory jedisConnectionFactory = 
    			new JedisConnectionFactory(redisSentinelConfiguration, jedisPoolConfig);
    	return jedisConnectionFactory;
    }
    
    @Bean
    @ConditionalOnBean(RedisStandaloneConfiguration.class)
    public JedisConnectionFactory jedisConnectionFactory(RedisStandaloneConfiguration redisStandaloneConfiguration) {
    	JedisConnectionFactory jedisConnectionFactory = 
    			new JedisConnectionFactory(redisStandaloneConfiguration);
    	return jedisConnectionFactory;
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
	@Bean
    public RedisTemplate<?,?> redisTemplate(RedisSerializer keySer, RedisSerializer valueSer,
    		JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        redisTemplate.setKeySerializer(keySer);
        redisTemplate.setValueSerializer(valueSer);
        redisTemplate.afterPropertiesSet();
        //redisTemplate.setEnableTransactionSupport(true);
        return redisTemplate;
    }
    
    @Bean
    public RedisClient redisClient() {
    	return new RedisClient();
   
    }

}
